using System;
using System.Collections.Generic;
using MonoTouch.Foundation;

namespace Ro.Norc.ServiceStackClient
{
	/// <summary>
	/// Implements the ServiceTransportInterface using the MonoTouch.Foundation.NSURLConnection class as a transport method
	/// </summary>
	public class ServiceTransportNSUrlConnection : ServiceTransportInterface
	{
		#region Events
		
		/// <summary>
		/// Occurs when the request execution finishes, either successfully or with an error
		/// </summary>
		public event ServiceTransportResponseDelegate TransportResponse;
		
		/// <summary>
		/// Occurs when a new data batch is received from the server and provides information about the total progress of the download phase
		/// </summary>
		public event ServiceTransportProgressDelegate TransportProgress;
		
		/// <summary>
		/// Occurs when a new data batch is sent to the server and provides information about the total progress of the upload phase
		/// </summary>
		public event ServiceTransportUploadProgressDelegate TransportUploadProgress;
		
		/// <summary>
		/// Occurs when an authentication challenge is received from the server, allowing the client code to provide the credentials
		/// </summary>
		public event ServiceTransportBasicHttpAuthPromptDelegate TransportBasicHttpAuthPrompt;
		
		#endregion
		
		#region Private fields
		
		/// <summary>
		/// A flag indicating whether this transport instance is currently busy serving a request or not
		/// </summary>
		private bool _loading = false;
		
		/// <summary>
		/// The currently active connection
		/// </summary>
		private NSUrlConnection _connection = null;
		
		/// <summary>
		/// The currently active request
		/// </summary>
		private ServiceTransportRequest _currentRequest = null;
		
		#endregion
		
		/// <summary>
		/// Executes a new request
		/// </summary>
		/// <param name='request'>
		/// The descriptor of the request that should be executed
		/// </param>
		/// <exception cref='ArgumentNullException'>
		/// If the request descriptor is null
		/// </exception>
		public void Send (ServiceTransportRequest request)
		{
			NSMutableUrlRequest _urlRequest = null;
			object _body = null;

			if (request == null) {
				throw new ArgumentNullException("request");
			}
			_currentRequest = request;
			
			_urlRequest = new NSMutableUrlRequest();
			_urlRequest.HttpMethod = request.Method;
			_urlRequest.ShouldHandleCookies = true;
			_urlRequest.Url = new NSUrl(request.Url);
			
			_body = request.Body;
			if (_body != null) {
				_urlRequest.Body = NSData.FromString(_body.ToString(), NSStringEncoding.UTF8);
			}
			_body = null;

			foreach (KeyValuePair<string, string> header in request.Headers) {
				_urlRequest[header.Key] = header.Value;
			}
			
			try {
				_connection = new NSUrlConnection(_urlRequest, new ServiceTransportNSUrlConnectionDelegate(this), true);
				_loading = true;
			} catch (Exception) {
				_loading = false;
				_currentRequest = null;
			}
		}
		
		/// <summary>
		/// Cancels the active request and frees any resources that were held while serving it
		/// </summary>
		public void Close ()
		{
			_loading = false;
			_currentRequest = null;
			if (_connection != null) {
				_connection.Cancel();
				_connection.Dispose();
				_connection = null;
			}
		}
		
		/// <summary>
		/// Infrastructure. Called by the delegate when the request execution finishes, either successfully or with an error
		/// </summary>
		/// <param name='response'>
		/// The response provided by the delegate
		/// </param>
		internal void OnResponse(ServiceTransportResponse response)
		{
			if (TransportResponse != null) {
				TransportResponse(this, new ServiceTransportResponseArgs(response));
			}
			Close();
		}
		
		/// <summary>
		/// Infrastructure. Called by the delegate when a new data batch is received from the server and provides the total progress of the download phase
		/// </summary>
		/// <param name='progress'>
		/// The total download progress
		/// </param>
		internal void OnProgress(double progress)
		{
			if (TransportProgress != null) {
				TransportProgress(this, new ServiceProgressArgs(progress));
			}
		}
		
		/// <summary>
		/// Infrastructure. Called by the delegate when a new data batch is sent to the server and provides the total progress of the upload phase
		/// </summary>
		/// <param name='progress'>
		/// The total download progress
		/// </param>
		internal void OnUploadProgress(double progress)
		{
			if (TransportUploadProgress != null) {
				TransportUploadProgress(this, new ServiceProgressArgs(progress));
			}
		}
		
		/// <summary>
		/// Infrastructure. Called by the delegate when an authentication challenge is received from the server
		/// </summary>
		/// <param name='prompt'>
		/// The authentication prompt
		/// </param>
		internal void OnBasicHttpAuthenticationPrompt(ServiceBasicHttpAuthPrompt prompt)
		{
			if (TransportBasicHttpAuthPrompt != null) {
				TransportBasicHttpAuthPrompt(this, new ServiceBasicHttpAuthPromptArgs(prompt));
			}
		}

		/// <summary>
		/// Infrastructure. Checks whether the request id being passed is the same as the id of the active request
		/// </summary>
		/// <returns>
		/// True if the requestId matches with the one of the active request, false otherwise
		/// </returns>
		internal bool IsRequestValid(string requestId)
		{
			if (_currentRequest == null) {
				return false;
			}
			return _currentRequest.RequestId == requestId;
		}
		
		/// <summary>
		/// Gets the descriptor of the active request being served by this transport instance. Returns null if no such request exists
		/// </summary>
		public ServiceTransportRequest CurrentRequest
		{
			get {
				return _currentRequest;
			}
		}
		
		/// <summary>
		/// Gets a value indicating whether this transport instance is currently busy serving a request or not
		/// </summary>
		public bool Loading {
			get {
				return _loading;
			}
		}
	}
}